﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MongoServices.Tests.TestHelpers;
using MongoServices.Tests.Stubs;
using Xunit;
using MongoDB.Bson;

namespace MongoServices.Tests.ReadWriteRepositoryTests
{
	public class ReadWriteRepositoryTests : TestBase
	{
		protected ReadWriteRepository<TestModelSimple> ReadWriteRepositoryTestModelSimple { get; private set; }

		public ReadWriteRepositoryTests()
		{
			Setup();
		}

		private void Setup()
		{
			ReadWriteRepositoryTestModelSimple = new ReadWriteRepositoryTestModelSimpleStub();
		}
	}

	public class WhenSaveWithModelIsCalled : ReadWriteRepositoryTests
	{
		[Fact]
		public void TheModelThatIsSavedCanBeRecalledFromTheDatabase()
		{
			var testData = "TestData";
			var model = new TestModelSimple { TestField = testData };
			ReadWriteRepositoryTestModelSimple.Save(model);

			var retrievedModels = CollectionTestModelSimple.FindAllAs<TestModelSimple>();

			Assert.Equal(1, retrievedModels.Count());
			Assert.Equal(testData, retrievedModels.First().TestField);
		}

		[Fact]
		public void TheModelThatIsMarkedForDeleteCannotBeRecalledFromTheDatabase()
		{
			var testData = "TestData";
			var model = new TestModelSimple { TestField = testData };
			CollectionTestModelSimple.Save(model);

			var retrievedModels = CollectionTestModelSimple.FindAllAs<TestModelSimple>().First();
			retrievedModels.MarkForDelete();
			ReadWriteRepositoryTestModelSimple.Save(retrievedModels);

			var retrievedModelsAfterDelete = CollectionTestModelSimple.FindAllAs<TestModelSimple>();

			Assert.Equal(0, retrievedModelsAfterDelete.Count());
		}

		[Fact]
		public void TheModelThatIsUnmarkedForDeleteCanBeRecalledFromTheDatabase()
		{
			var testData = "TestData";
			var model = new TestModelSimple { TestField = testData };
			CollectionTestModelSimple.Save(model);

			var retrievedModel = CollectionTestModelSimple.FindAllAs<TestModelSimple>().First();
			retrievedModel.MarkForDelete();
			retrievedModel.UnmarkForDelete();
			ReadWriteRepositoryTestModelSimple.Save(retrievedModel);

			var retrievedModelsAfterDelete = CollectionTestModelSimple.FindAllAs<TestModelSimple>();

			Assert.Equal(1, retrievedModelsAfterDelete.Count());
			Assert.Equal(testData, retrievedModelsAfterDelete.First().TestField);
		}

		[Fact]
		public void TheModelThatAlreadyExistsGetsUpdated()
		{
			var testData = "TestData";
			var testDataUpdated = "TestDataUpdated";
			var model = new TestModelSimple { TestField = testData };
			CollectionTestModelSimple.Save(model);

			var retrievedModel = CollectionTestModelSimple.FindAllAs<TestModelSimple>().First();
			retrievedModel.TestField = testDataUpdated;
			ReadWriteRepositoryTestModelSimple.Save(retrievedModel);

			var retrievedModelsAfterUpdate = CollectionTestModelSimple.FindAllAs<TestModelSimple>();

			Assert.Equal(1, retrievedModelsAfterUpdate.Count());
			Assert.Equal(testDataUpdated, retrievedModelsAfterUpdate.First().TestField);
		}

		[Fact]
		public void TheModelThatIsSavedWithASpecificIdIsAddedToTheDatabase()
		{
			var testData = "TestData";
			var id = ObjectId.GenerateNewId();
			var model = new TestModelSimple { Id = id, TestField = testData };
			ReadWriteRepositoryTestModelSimple.Save(model);

			var retrievedModels = CollectionTestModelSimple.FindAllAs<TestModelSimple>();

			Assert.Equal(1, retrievedModels.Count());
			Assert.Equal(id, retrievedModels.First().Id);
		}
	}

	public class WhenDeleteByIdIsCalled : ReadWriteRepositoryTests
	{
		[Fact]
		public void TheCorrectModelIsRemovedFromTheDatabase()
		{
			var id1 = ObjectId.GenerateNewId();
			var id2 = ObjectId.GenerateNewId();
			var id3 = ObjectId.GenerateNewId();

			var testModelCollection = new List<TestModelSimple> {
				new TestModelSimple { Id = id1, TestField = "TestData1" },
				new TestModelSimple { Id = id2, TestField = "TestData2" },
				new TestModelSimple { Id = id3, TestField = "TestData3" }
			};

			testModelCollection.ForEach(m => CollectionTestModelSimple.Save(m));

			ReadWriteRepositoryTestModelSimple.DeleteById(id1);

			var retrievedModel = CollectionTestModelSimple.FindAllAs<TestModelSimple>();

			Assert.False(retrievedModel.Any(m => m.Id == id1));
			Assert.True(retrievedModel.Any(m => m.Id == id2));
			Assert.True(retrievedModel.Any(m => m.Id == id3));
		}
	}
}
